package api

import (
	"sync"
	"time"

	"gitee.com/sillyman/mixlog"
	"github.com/gorilla/websocket"
	"github.com/labstack/echo/v4"
	"github.com/satori/go.uuid"

	"gitee.com/sillyman/PingHostsWebView/models"
	"gitee.com/sillyman/PingHostsWebView/modules/db"
)

var (
	wsUpgrader                         = websocket.Upgrader{}
	wsBroLatestPingSummariesConns      = make(map[string]*websocket.Conn)
	wsBroLatestPingSummariesConnsMutex = new(sync.Mutex)
	wsBroLatestPingSummariesOnce       = new(sync.Once)
)

// WsBroLatestPingSummaries 通过websocket广播最新的 pinger 结果
func WsBroLatestPingSummaries(c echo.Context) error {
	wsConn, err := wsUpgrader.Upgrade(c.Response(), c.Request(), nil)
	if err != nil {
		return newHttpErrServer(err)
	}
	defer func() {
		if wsConn != nil {
			wsConn.Close()
		}
	}()

	clientUUID := uuid.NewV4().String()
	wsBroLatestPingSummariesConns[clientUUID] = wsConn
	wsBroLatestPingSummariesOnce.Do(broLatestPingSummaries)

	for {
		if _, _, err := wsConn.ReadMessage(); err != nil { // 接收客户端的消息，但是不做任何处理
			wsBroLatestPingSummariesConnsMutex.Lock()
			delete(wsBroLatestPingSummariesConns, clientUUID)
			wsBroLatestPingSummariesConnsMutex.Unlock()

			break
		}
	}
	return nil
}

// broLatestPingSummaries 一秒广播一次
func broLatestPingSummaries() {
	t := time.NewTicker(time.Second)
	for range t.C {
		if len(wsBroLatestPingSummariesConns) == 0 {
			wsBroLatestPingSummariesOnce = new(sync.Once)
			break
		}

		latestNews := make([]models.PingSummary, 0, 128)
		// 读取 2 秒更新的所有记录
		dbConn := db.MustObtainMainDB()
		if err := dbConn.Where("updated_at > ?", time.Now().Add(-2*time.Second)).Find(&latestNews).Error; err != nil {
			mixlog.Fatalf("读数据库失败", err)
		}
		db.ReleaseMainDB()

		wsBroLatestPingSummariesConnsMutex.Lock()
		for clientUUID, wsConn := range wsBroLatestPingSummariesConns {
			if wsConn == nil {
				delete(wsBroLatestPingSummariesConns, clientUUID)
				continue
			}

			if err := wsConn.WriteJSON(latestNews); err != nil {
				// 浏览器断开了连接，通常是浏览器执行了 F5 刷新，导致 ws 中断了，但是浏览器又重新创建了新的连接。
				delete(wsBroLatestPingSummariesConns, clientUUID)
			}
		}

		wsBroLatestPingSummariesConnsMutex.Unlock()
	}
}
